/*
 *  Copyright (C) 2004 Kai Toedter
 *  kai@toedter.com
 *  www.toedter.com
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package com.pkdl.datepicker;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

/**
 * JCalendar is a bean for entering a date by choosing the year, month and day.
 * 
 * @author Kai Toedter
 * @version 1.2
 */
public class JCalendar extends JPanel implements PropertyChangeListener {

	private Calendar calendar;
	/** the day chooser */
	protected JDayChooser dayChooser;
	private boolean initialized = false;
	/** indicates if weeks of year shall be visible */
	protected boolean weekOfYearVisible = true;
	/** the locale */
	protected Locale locale;
	/** the month chooser */
	protected JMonthChooser monthChooser;
	private JPanel monthYearPanel;
	/** the year chhoser */
	protected JYearChooser yearChooser;

	/**
	 * Default JCalendar constructor.
	 */
	public JCalendar() {
		this(null, null, true, true);
	}

	/**
	 * JCalendar constructor specifying the month spinner type.
	 * 
	 * @param monthSpinner
	 *            false, if no month spinner should be used
	 */
	public JCalendar(boolean monthSpinner) {
		this(null, null, monthSpinner, true);
	}

	/**
	 * JCalendar constructor which allows the initial date to be set.
	 * 
	 * @param date
	 *            the date
	 */
	public JCalendar(Date date) {
		this(date, null, true, true);
	}

	/**
	 * JCalendar constructor specifying both the initial date and the month
	 * spinner type.
	 * 
	 * @param date
	 *            the date
	 * @param monthSpinner
	 *            false, if no month spinner should be used
	 */
	public JCalendar(Date date, boolean monthSpinner) {
		this(date, null, monthSpinner, true);
	}

	/**
	 * JCalendar constructor specifying both the initial date and locale.
	 * 
	 * @param date
	 *            the date
	 * @param locale
	 *            the new locale
	 */
	public JCalendar(Date date, Locale locale) {
		this(date, locale, true, true);
	}

	/**
	 * JCalendar constructor with month spinner parameter.
	 * 
	 * @param date
	 *            the date
	 * @param locale
	 *            the locale
	 * @param monthSpinner
	 *            false, if no month spinner should be used
	 * @param weekOfYearVisible
	 *            true, if weeks of year shall be visible
	 */
	public JCalendar(Date date, Locale locale, boolean monthSpinner,
			boolean weekOfYearVisible) {
		// needed for setFont() etc.
		this.dayChooser = null;
		this.monthChooser = null;
		this.yearChooser = null;
		this.weekOfYearVisible = weekOfYearVisible;

		this.locale = locale;

		if (locale == null) {
			this.locale = Locale.getDefault();
		}

		this.calendar = Calendar.getInstance();

		this.setLayout(new BorderLayout());
		this.monthYearPanel = new JPanel();
		this.monthYearPanel.setLayout(new BorderLayout());

		this.dayChooser = new JDayChooser(weekOfYearVisible);
		this.monthChooser = new JMonthChooser(monthSpinner);
		this.yearChooser = new JYearChooser();
		this.monthChooser.setYearChooser(this.yearChooser);

		this.monthYearPanel.add(this.monthChooser, BorderLayout.WEST);
		this.monthYearPanel.add(this.yearChooser, BorderLayout.CENTER);
		this.monthYearPanel.setBorder(BorderFactory.createEmptyBorder());

		// dayChooser.addPropertyChangeListener(this);
		this.monthChooser.setDayChooser(this.dayChooser);
		this.monthChooser.addPropertyChangeListener(this);
		this.yearChooser.setDayChooser(this.dayChooser);
		this.yearChooser.addPropertyChangeListener(this);
		this.add(this.monthYearPanel, BorderLayout.NORTH);
		this.add(this.dayChooser, BorderLayout.CENTER);

		// Set the initialized flag before setting the calendar. This will
		// cause the other components to be updated properly.
		if (date != null) {
			this.calendar.setTime(date);
		}

		this.initialized = true;
		this.setCalendar(this.calendar);
	}

	/**
	 * JCalendar constructor allowing the initial locale to be set.
	 * 
	 * @param locale
	 *            the new locale
	 */
	public JCalendar(Locale locale) {
		this(null, locale, true, true);
	}

	/**
	 * JCalendar constructor specifying both the locale and the month spinner.
	 * 
	 * @param locale
	 *            the locale
	 * @param monthSpinner
	 *            false, if no month spinner should be used
	 */
	public JCalendar(Locale locale, boolean monthSpinner) {
		this(null, locale, monthSpinner, true);
	}

	/**
	 * Creates a JFrame with a JCalendar inside and can be used for testing.
	 * 
	 * @param s
	 *            The command line arguments
	 */
	/**
	 * Returns the calendar property.
	 * 
	 * @return the value of the calendar property.
	 */
	public Calendar getCalendar() {
		return this.calendar;
	}

	/**
	 * Returns a Date object.
	 * 
	 * @return a date object constructed from the calendar property.
	 */
	public Date getDate() {
		return new Date(this.calendar.getTimeInMillis());
	}

	/**
	 * Gets the dayChooser attribute of the JCalendar object
	 * 
	 * @return the dayChooser value
	 */
	public JDayChooser getDayChooser() {
		return this.dayChooser;
	}

	/**
	 * Returns the color of the decoration (day names and weeks).
	 * 
	 * @return the color of the decoration (day names and weeks).
	 */
	public Color getDecorationBackgroundColor() {
		return this.dayChooser.getDecorationBackgroundColor();
	}

	/**
	 * Returns the locale.
	 * 
	 * @return the value of the locale property.
	 * 
	 * @see #setLocale
	 */
	public Locale getLocale() {
		return this.locale;
	}

	/**
	 * Gets the monthChooser attribute of the JCalendar object
	 * 
	 * @return the monthChooser value
	 */
	public JMonthChooser getMonthChooser() {
		return this.monthChooser;
	}

	/**
	 * Returns "JCalendar".
	 * 
	 * @return "JCalendar"
	 */
	public String getName() {
		return "JCalendar";
	}

	/**
	 * Returns the Sunday foreground.
	 * 
	 * @return Color the Sunday foreground.
	 */
	public Color getSundayForeground() {
		return this.dayChooser.getSundayForeground();
	}

	/**
	 * Returns the weekday foreground.
	 * 
	 * @return Color the weekday foreground.
	 */
	public Color getWeekdayForeground() {
		return this.dayChooser.getWeekdayForeground();
	}

	/**
	 * Gets the yearChooser attribute of the JCalendar object
	 * 
	 * @return the yearChooser value
	 */
	public JYearChooser getYearChooser() {
		return this.yearChooser;
	}

	/**
	 * Gets the visibility of the decoration background.
	 * 
	 * @return true, if the decoration background is visible.
	 */
	public boolean isDecorationBackgroundVisible() {
		return this.dayChooser.isDecorationBackgroundVisible();
	}

	/**
	 * Gets the visibility of the decoration border.
	 * 
	 * @return true, if the decoration border is visible.
	 */
	public boolean isDecorationBordersVisible() {
		return this.dayChooser.isDecorationBordersVisible();
	}

	/**
	 * Returns true, if enabled.
	 * 
	 * @return true, if enabled.
	 */
	public boolean isEnabled() {
		return super.isEnabled();
	}

	/**
	 * Indicates if the weeks of year are visible..
	 * 
	 * @return boolean true, if weeks of year are visible
	 */
	public boolean isWeekOfYearVisible() {
		return this.dayChooser.isWeekOfYearVisible();
	}

	/**
	 * JCalendar is a PropertyChangeListener, for its day, month and year
	 * chooser.
	 * 
	 * @param evt
	 *            the property change event
	 */
	public void propertyChange(PropertyChangeEvent evt) {
		if (this.calendar != null) {
			Calendar c = (Calendar) this.calendar.clone();
		}
	}

	/**
	 * Sets the background color.
	 * 
	 * @param bg
	 *            the new background
	 */
	public void setBackground(Color bg) {
		super.setBackground(bg);

		if (this.dayChooser != null) {
			this.dayChooser.setBackground(bg);
		}
	}

	/**
	 * Sets the calendar property. This is a bound property.
	 * 
	 * @param c
	 *            the new calendar
	 * 
	 * @see #getCalendar
	 */
	public void setCalendar(Calendar c) {
		this.setCalendar(c, true);
	}

	/**
	 * Sets the calendar attribute of the JCalendar object
	 * 
	 * @param c
	 *            the new calendar value
	 * @param update
	 *            the new calendar value
	 */
	private void setCalendar(Calendar c, boolean update) {
		Calendar oldCalendar = this.calendar;
		this.calendar = c;

		if (update) {
			// Thanks to Jeff Ulmer for correcting a bug in the sequence :)
			this.yearChooser.setYear(c.get(Calendar.YEAR));
			this.monthChooser.setMonth(c.get(Calendar.MONTH));
			this.dayChooser.setDay(c.get(Calendar.DATE));
		}

		this.firePropertyChange("calendar", oldCalendar, this.calendar);
	}

	/**
	 * Sets the date. Fires the property change "date".
	 * 
	 * @param date
	 *            the new date.
	 */
	public void setDate(Date date) {
		Date oldDate = this.calendar.getTime();
		this.calendar.setTime(date);

		this.yearChooser.setYear(this.calendar.get(Calendar.YEAR));
		this.monthChooser.setMonth(this.calendar.get(Calendar.MONTH));
		this.dayChooser.setDay(this.calendar.get(Calendar.DATE));

		this.firePropertyChange("date", oldDate, date);
	}

	/**
	 * Sets the background of days and weeks of year buttons.
	 * 
	 * @param decorationBackgroundColor
	 *            the background color
	 */
	public void setDecorationBackgroundColor(Color decorationBackgroundColor) {
		this.dayChooser.setDecorationBackgroundColor(decorationBackgroundColor);
	}

	/**
	 * Sets the decoration background visible.
	 * 
	 * @param decorationBackgroundVisible
	 *            true, if the decoration background should be visible.
	 */
	public void setDecorationBackgroundVisible(
			boolean decorationBackgroundVisible) {
		this.dayChooser
				.setDecorationBackgroundVisible(decorationBackgroundVisible);
		this.setLocale(this.locale); // hack for doing complete new layout :)
	}

	/**
	 * Sets the decoration borders visible.
	 * 
	 * @param decorationBordersVisible
	 *            true, if the decoration borders should be visible.
	 */
	public void setDecorationBordersVisible(boolean decorationBordersVisible) {
		this.dayChooser.setDecorationBordersVisible(decorationBordersVisible);
		this.setLocale(this.locale); // hack for doing complete new layout :)
	}

	/**
	 * Enable or disable the JCalendar.
	 * 
	 * @param enabled
	 *            the new enabled value
	 */
	public void setEnabled(boolean enabled) {
		super.setEnabled(enabled);

		if (this.dayChooser != null) {
			this.dayChooser.setEnabled(enabled);
			this.monthChooser.setEnabled(enabled);
			this.yearChooser.setEnabled(enabled);
		}
	}

	/**
	 * Sets the font property.
	 * 
	 * @param font
	 *            the new font
	 */
	public void setFont(Font font) {
		super.setFont(font);

		if (this.dayChooser != null) {
			this.dayChooser.setFont(font);
			this.monthChooser.setFont(font);
			this.yearChooser.setFont(font);
		}
	}

	/**
	 * Sets the foreground color.
	 * 
	 * @param fg
	 *            the new foreground
	 */
	public void setForeground(Color fg) {
		super.setForeground(fg);

		if (this.dayChooser != null) {
			this.dayChooser.setForeground(fg);
			this.monthChooser.setForeground(fg);
			this.yearChooser.setForeground(fg);
		}
	}

	/**
	 * Sets the locale property. This is a bound property.
	 * 
	 * @param l
	 *            the new locale value
	 * 
	 * @see #getLocale
	 */
	public void setLocale(Locale l) {
		if (!this.initialized) {
			super.setLocale(l);
		} else {
			Locale oldLocale = this.locale;
			this.locale = l;
			this.dayChooser.setLocale(this.locale);
			this.monthChooser.setLocale(this.locale);
			this.firePropertyChange("locale", oldLocale, this.locale);
		}
	}

	/**
	 * Sets the Sunday foreground.
	 * 
	 * @param sundayForeground
	 *            the sundayForeground to set
	 */
	public void setSundayForeground(Color sundayForeground) {
		this.dayChooser.setSundayForeground(sundayForeground);
	}

	/**
	 * Sets the weekday foreground.
	 * 
	 * @param weekdayForeground
	 *            the weekdayForeground to set
	 */
	public void setWeekdayForeground(Color weekdayForeground) {
		this.dayChooser.setWeekdayForeground(weekdayForeground);
	}

	/**
	 * Sets the week of year visible.
	 * 
	 * @param weekOfYearVisible
	 *            true, if weeks of year shall be visible
	 */
	public void setWeekOfYearVisible(boolean weekOfYearVisible) {
		this.dayChooser.setWeekOfYearVisible(weekOfYearVisible);
		this.setLocale(this.locale); // hack for doing complete new layout :)
	}
}
